package cn.tedu.net.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// 本类用于改造售票案例,用于解决多线程编程安全问题
// 需求:4个线程同时卖票,共计卖票100张
//问题一:重卖:一张票卖给了多个人
//问题二:超卖:卖出了0 -1 -2张票
public class TestSaleTicketsV2 {
    public static void main(String[] args) {
        SaleTicketsV2 saleTicketsV2=new SaleTicketsV2();
//        Thread t1=new Thread(saleTicketsV2);
//        Thread t2=new Thread(saleTicketsV2);
//        Thread t3=new Thread(saleTicketsV2);
//        Thread t4=new Thread(saleTicketsV2);
//        //如果只创建一个线程对象,是单线程编程效果
//        t1.start();
//        t2.start();
//        t3.start();
//        t4.start();
        //线程池ExecutorService:用来存放线程的池子,新建/就绪/启动/阻塞/结束
        //用来造池子的工具叫做Executors:用来创建线程池对象
        ExecutorService poll = Executors.newFixedThreadPool(4);
        for (int i = 0; i < 4; i++) {
            poll.execute(saleTicketsV2);
        }
    }
}
//1.自定义线程类
//多线程中出现数据不安全的前提: 多线程运行+有共享数据+多条语句操作共享数据
//解决方案:同步锁:相当于把容易出现问题的代码锁起来,排队进入,钥匙让进去的数据对象拥有
class SaleTicketsV2 implements Runnable{
    static int tickets=100;
    Object o=new Object();
    //创建一个唯一的锁对象,之后不论哪个线程进入同步代码块都是用的这个对象
    @Override
    //如果一个方法中的代码都被同步了,我们可以把这个方法改为同步方法,同步方法使用的锁对象是this
    //synchronized  public void run() {
    public void run() {
        while (true) {
            //同步代码块 指同一时间这个代码块只能被一个线程享有
            synchronized(this){
            if (tickets > 0) {
                try {
                    Thread.sleep(10);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "=" + tickets--);
            }else if(tickets<=0){
                break;
                }
            }
        }
    }
}